/*@parser.c::*/
/** 
 * Simplified Pascal in the EBNF:
 * 
 * program -> PROGRAM ID ';' block '.'
 * 
 * block -> declarations stmtblock
 * 
 * declarations -> 
 *    [CONST ID = constant';' {ID = constant';'}]
 *    [TYPE ID = type';' {ID = type';'}]
 *    [VAR idlist ':' type';' {idlist ':' type';'}]
 *    modules
 *
 * modules -> {procedure | function}
 * 
 * procedure -> PROCEDURE ID formalparm';' block ';'
 * 
 * function -> FUNCTION ID formalparm ':' typeid ';'block';'
 * 
 * stmtblock -> BEGIN stmtlist END
 * 
 * stmtlist -> stmt {';' stmt}
 * 
 * stmt -> stmtblock | ifstmt | whilestmt | repstmt | forstmt
 *      | gotostmt | casestmt | idstmt
 */

/** further simplifications for today only:
 * 
 * program -> PROGRAM ID ';' block '.'
 * 
 * block -> declarations stmtblock
 * 
 * declarations -> 
 *    [VAR idlist ':' type';' {idlist ':' type';'}]
 *    modules
 *
 * modules -> {procedure | function}
 * 
 * procedure -> PROCEDURE ID formalparm';' block ';'
 * 
 * function -> FUNCTION ID formalparm ':' typeid ';'block';'
 * 
 * stmtblock -> BEGIN stmtlist END
 * 
 * stmtlist -> stmt {';' stmt}
 * 
 * stmt -> stmtblock | ifstmt | whilestmt | repstmt | idstmt | ""
 */
#include <string.h>
#include <stdio.h>
#include <lexer.h>
#include <parser.h>
#include <symtab.h>
#include <keywords.h>
#include <tokens.h>

#define VARTYPE 1
#define FUNCTYPE 2
#define PARMTYPE 3

#define MAXSYMLIST      0x10000
#define MAXIDSIZ        32
char symlist[MAXSYMLIST][MAXIDSIZ];
int sympos,procpos,funcpos,idposi,idposf;
int idtype;

int lblNbr = 1;
int lexlevel;
int dtype;

char *debug;

/* program -> PROGRAM ID ';' block '.'*/
void program(void)
{
  /** */lexlevel = 0;/** */
  match(PROGRAM); 
  match(ID);
  match(';');
  block();
  match('.');
}

/* block -> declarations stmtblock*/
void block(void)
{
  /** */lexlevel++;/** */
  declarations();
  stmtblock();
  /** */lexlevel--;/** */
}

/* declarations -> 
*    [VAR idlist ':' type';' {idlist ':' type';'}]
*    modules*/
void declarations(void)
{
  if(lookahead==VAR){
    /** */idtype = VARTYPE;/** */
    match(VAR); 
rep_idlist:
    /** */sympos=0/** */;
    idlist();match(':');type();match(';');
    if(lookahead==ID)goto rep_idlist;
  }
  modules();
}

/* modules -> {procedure | function} */
void modules(void)
{
  while(lookahead==PROCEDURE || lookahead==FUNCTION){
    if(lookahead==PROCEDURE)procedure();
    else function();
  }
}

/* procedure -> PROCEDURE ID formalparm';' block ';' */
void procedure(void)
{
  int i;
  match(dtype=PROCEDURE);
  /** **/
  procpos = sympos;
  strcpy(symlist[sympos++],lexeme);
  /** **/
  match(ID);
  /** */lexlevel++;/** */
  formalparm();
  /** */lexlevel--;/** */
  match(';');
  /** */
  if(symtab_lookup(symlist[procpos],lexlevel)) {
    fprintf(stderr,"symbol \"%s\" already declared\n",
      symlist[procpos]);
  }else{
    symtab_insert(symlist[procpos],dtype,lexlevel);
  }
  /** */
  
  block();
  match(';');
}

/* function -> FUNCTION ID formalparm ':' typeid ';'block';' */
void function(void)
{
  match(FUNCTION);
  /** */
  funcpos = sympos;
  strcpy(symlist[sympos++],lexeme);
  /** **/
  match(ID);
  /** */lexlevel++;/** */
  formalparm();
  /** */lexlevel--;/** */
  match(':');
  /** */idtype = FUNCTYPE;/** */
  type(); match(';');
  block();
  match(';');
}

/* formalparm -> '(' parlist ')' | "" */
void formalparm(void){
  if(lookahead =='('){
    match('(');
    parlist();
    match(')');
  }
}

/* parlist -> pardecl {';' pardecl}*/
void parlist(void){
  pardecl();
  while(lookahead == ';'){
    match(';');
    pardecl();
  }
}

/*pardecl -> [VAR] idlist ':' type | heading */
void pardecl(void){
  /** */idtype = PARMTYPE;/** */
  switch(lookahead){
    case VAR:
      match(VAR);
      idlist();
      match(':');
      type();
    break;
    case ID:
      idlist();
      match(':');
      type();
    break;
    
    case PROCEDURE:
    case FUNCTION:
      heading();
    break;
  }
}

/* heading -> procedureheader | functionheader*/
void heading(void){
  if(lookahead==PROCEDURE) procedureheader();
  else functionheader();
}

/* procedureheader -> PROCEDURE ID formalparm */
void procedureheader(void){
  match(PROCEDURE);match(ID);formalparm();
}

/* functionheader -> FUNCTION ID formalparm ':' typeid */
void functionheader(void){
  match(FUNCTION);match(ID);formalparm();match(':');type();
}

/* idlist -> ID {','ID}*/
void idlist(void)
{
  /** */idposi = sympos;/** */
  /** **/strcpy(symlist[sympos++],lexeme);/** **/
  match(ID);
  while(lookahead==','){
    match(',');
    /** **/strcpy(symlist[sympos++],lexeme);/** **/
    match(ID);
  }
  /** */idposf = sympos;/** */
}

/* type -> INTEGER | REAL */
void type(void)
{
  int i;
  switch(lookahead){
    case INTEGER:
      match(dtype=INTEGER);
      break;
    default:
      match(dtype=REAL);
  }
  
  /** */
  switch(idtype){
    case VARTYPE:
    case PARMTYPE:
      for(i=idposi;i<idposf;i++) {
	if(symtab_lookup(symlist[i],lexlevel)) {
	  fprintf(stderr,"symbol \"%s\" already declared\n",
	    symlist[i]);
	}else{
	  symtab_insert(symlist[i],dtype,lexlevel);
	}
      }
      break;
    case FUNCTYPE:
      if(symtab_lookup(symlist[funcpos],lexlevel)) {
	fprintf(stderr,"symbol \"%s\" already declared\n",
	  symlist[funcpos]);
      }else{
	symtab_insert(symlist[funcpos],dtype,lexlevel);
      }
      break;
  }
  /** */
}

/* stmt -> stmtblock | ifstmt | whilestmt | repstmt | idstmt | "" */
void stmt(void)
{
  switch(lookahead){
    case BEGIN:
      stmtblock();
      break;
    case IF:
      ifstmt();
      break;
    case WHILE:
      whilestmt();
      break;
    case REPEAT:
      repstmt();
      break;
    case ID:
      idstmt();
      break;
  }
}

/* stmtblock -> BEGIN stmtlist END */
void stmtblock(void)
{
  match(BEGIN);stmtlist();match(END);
}

/* stmtlist -> stmt {';' stmt} */
void stmtlist(void)
{
  stmt();
  while(lookahead==';'){
    match(';');stmt();
  }
}

/* ifstmt -> IF expression THEN stmt [ELSE stmt] */
void ifstmt(void)
{
  match(IF);expression();match(THEN);
    /** */lexlevel++;/** */
    stmt();
    /** */lexlevel--;/** */
  if(lookahead==ELSE) {
    match(ELSE);
    /** */lexlevel++;/** */
    stmt();
    /** */lexlevel--;/** */
  }
}

/* whilestmt -> WHILE expression DO stmt */
void whilestmt(void)
{
  match(WHILE); expression(); match(DO); 
  /** */lexlevel++;/** */
  stmt();
  /** */lexlevel--;/** */
}

/* repstmt -> REPEAT stmt {';' stmt} UNTIL expression */
void repstmt(void)
{
  match(REPEAT);
   /** */lexlevel++;/** */
  stmt();
  while(lookahead == ';'){
    match(';'); stmt();
  }
  match(UNTIL); expression();
  /** */lexlevel--;/** */
}

/* idstmt -> ID assgnmt expression */
void idstmt(void)
{
  match(ID); match(ASSGNMT); expression();
}


/* relationalop -> '=' | '<' | '>' | GEQ | LEQ | NEQ */
isrelationalop(token_t token)
{
  switch(token){
    case '=':
    case '>':
    case '<':
    case GEQ:
    case LEQ:
    case NEQ:
      return token;
  }
  return 0;
}

/* addop -> '+' | '-' | OR */
isaddop(token_t token)
{
  switch(token){
    case '+':
    case '-':
    case OR:
      return token;
  }
  return 0;
}

/* mulop -> '*' | '/' | AND | DIV | MOD */
ismulop(token_t token)
{
  switch(token){
    case '*':
    case '/':
    case AND:
    case DIV:
    case MOD:
      return token;
  }
  return 0;
}

/* expression -> expr [relationalop expr] */
void expression(void)
{
  expr();
  if(isrelationalop(lookahead)){
    match(lookahead); expr();
  }
}

/* expr -> [-] term {addop term} */
void expr(void)
{
  if(lookahead=='-'){
    match('-');
  }
  term();
  while(isaddop(lookahead)){
    match(lookahead); term();
  }
}

/* term -> factor {mulop factor} */
void term(void)
{
  factor();
  while(ismulop(lookahead)){
    match(lookahead); factor();
  }
}

/* factor -> UINT | FLOAT | ID ['(' exprlist ')'] | '(' expr ')' | NOT factor */
void factor(void){
    switch(lookahead){
      case UINT:
        match(UINT);break;
      case FLOAT:
        match(FLOAT);break;
      case ID:
        match(ID);
        if(lookahead=='('){
          match('(');exprlist();match(')');
        }
        break;
      case '(':
        match('(');expression();match(')');break;
      default:
        match(NOT);factor();
    }
}

/* exprlist -> expression {',' expression} */
void exprlist(void)
{
  expression();
  while(lookahead==','){
    match(',');expression();
  }
}
